﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace XNAREUSABLES
{
    /// <summary>
    /// All 3D Objects contain one of these for the view.
    /// </summary>
    public class _3D_Entity
    {
        private String           m_Name;                     // The name of this entity.
        private String             Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }                     //Get/Set the name of this entity.
        private Primative_Bundle m_Primatives;               // The primative bundle of this entity.
        private Primative_Bundle   Primatives
        {
            get { return m_Primatives; }
            set { m_Primatives = value; }
        }                // Get/Set the primative bundle of this entity.
        private Quaternion       m_Orientation;              // The orientation of this entity.
        private Quaternion         Orientation
        {
            get
            {
                return m_Orientation;
            }
            set
            {
                m_Orientation = value;
                m_World_isDirty = true;
            }
        }              // Get/Set the orientation of this entity.
        private Matrix             Rotation
        {
            get
            {
                return Matrix.CreateFromQuaternion(Orientation);
            }
        }                 // Get the orientation matrix.
        private Vector3          m_Position;                 // The position of this entity.
        private Vector3            Position
        {
            get { return m_Position; }
            set
            {
                m_Position = value;
                m_World_isDirty = true;
            }
        }                 // Get/Set the position of this entity.
        private Matrix             Translation
        {
            get { return Matrix.CreateTranslation(Position); }
        }              // Get translation matrix  based on posistion.
        private float            m_Size;                     // The size (scale) of the ship body.
        private float              Size
        {
            get { return m_Size; }
            set
            {
                m_Size = value;
                m_World_isDirty = true;
            }
        }                     // Get/Set size (scale).
        private Matrix             Scale
        {
            get
            {
                return Matrix.CreateScale(Size);
            }
        }                    // Get the scale matrix for rendering.
        private Matrix           m_World;                    // The world matrix of this entity.
        private bool             m_World_isDirty = true;     // Does the world matrix need to be updated?  
        private Matrix             World
        {
            get
            {
                if (m_World_isDirty)
                {
                    m_World = Matrix.Invert(Matrix.Identity);
                    m_World *= Scale;
                    m_World *= Rotation;
                    m_World *= Translation;

                    m_World_isDirty = false;
                    return m_World;
                }
                else
                {
                    return m_World;
                }
            }
        }                    // Get the world matrix.

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="Name">The name of this object (for debug purposes).</param>
        /// <param name="PrimativeBundle">The primative bundle of this object.</param>
        /// <param name="Position">The world position of this object.</param>
        /// <param name="Orientation">The world orientation of this object.</param>
        /// <param name="ScaleSize">The scale size of this object.</param>
        public _3D_Entity(String Name, Primative_Bundle PrimativeBundle, Vector3 Position, Quaternion Orientation, float ScaleSize = 1.0f)
        {
            // Associate name.
            this.Name = Name;

            // Associate primative bundle.
            this.Primatives = PrimativeBundle;
            
            // Associate position.
            this.Position = Position;
            
            // Associate orientation.
            this.Orientation = Orientation;

            // Associate size.
            this.Size = ScaleSize;
        }

        public void Yaw_Self      (float Rads)
        {
            Orientation *= Quaternion.CreateFromAxisAngle(Vector3.Up, Rads);
        }
        public void Roll_Self     (float Rads)
        {
            Orientation *= Quaternion.CreateFromAxisAngle(Vector3.Forward, Rads);
        }
        public void Pitch_Self    (float Rads)
        {
            Orientation *= Quaternion.CreateFromAxisAngle(Vector3.Right, Rads);
        }
        public void Forward_Self  (float Distance)
        {
           Position += Vector3.Transform(new Vector3(0, 0, Distance), Orientation);
        }
        public void Backward_Self (float Distance)
        {
            Position +=  Vector3.Transform(new Vector3(0, 0, -Distance), Orientation);
        }
        public void Left_Self     (float Distance)
        {
            //Todo: this might not be right
            Position += Vector3.Transform(new Vector3(-Distance, 0, 0), Orientation);
        }
        public void Right_Self    (float Distance)
        {
            //Todo: this might not be right
            Position += Vector3.Transform(new Vector3(Distance, 0, 0), Orientation);
        }
        public void Up_Self       (float Distance)
        {
            //Todo: this might not be right
            Position += Vector3.Transform(new Vector3(0, Distance, 0), Orientation);
        }
        public void Down_Self     (float Distance)
        {
            //Todo: this might not be right
            Position += Vector3.Transform(new Vector3(0, -Distance, 0), Orientation);
        }

        // DO THESE
        public void Yaw_World     (float Rads)
        {

        }
        public void Roll_World    (float Rads)
        {

        }
        public void Pitch_World   (float Rads)
        {

        }
        public void Forward_World (float Distance)
        {

        }
        public void Backward_World(float Distance)
        {

        }
        public void Left_World    (float Distance)
        {

        }
        public void Right_World   (float Distance)
        {

        }
        public void Up_World      (float Distance)
        {

        }
        public void Down_World    (float Distance)
        {

        }

        /// <summary>
        /// If the world matrix has been manipulated in any way, it needs to be cleaned.
        /// Cleaning will occur automatically when the render function calls it, however
        /// it is best practice to update all view calculations before the render cycle,
        /// during the update cycle.
        /// </summary>
        public void CLEAN()
        {
            // Update the world matrix by calling its property
            Matrix null_mat = World;
        }

        /// <summary>
        /// Render the entity to screen.
        /// </summary>
        public void RENDER(CameraInstance Camera)
        {
            Effect Shader = ShaderPool.Get(0);
            Shader.CurrentTechnique = Shader.Techniques["WireFrame"];
            Shader.Parameters["World"].SetValue(World);
            Shader.Parameters["View"].SetValue(Camera.View);
            Shader.Parameters["Projection"].SetValue(Camera.Projection);

            // Set pass
            Shader.CurrentTechnique.Passes["WireFrameCW"].Apply();

            //Render
            Primatives.RENDER();
        }
    }
}
